package com.pocket.insight.base.sql.dialect;

import com.pocket.insight.base.mql.MDF;
import com.pocket.insight.base.mql.TypedVal;
import com.pocket.insight.base.utils.SqlUtil;
import org.apache.commons.lang3.StringUtils;

import java.util.*;

/**
 * 描述：
 *
 * @author shuize
 * @create 2024/11/30
*/
public class MysqlDialect extends BaseDialect {
    public static final    int              MYSQL_IDENTIFIER_MAX_LENGTH     = 64;
    public static final    int              MYSQL_DEFAULT_NUMERIC_PRECISION = 65;
    public static final    int              MYSQL_DEFAULT_NUMERIC_SCALE     = 10;
    protected static final Map<MDF, String> PRESETS                         = new TreeMap<>() {};
    private                Map<MDF, String> presets;
    private                int              seriesNum                       = 0;

    public void setSeriesNum(int seriesNum) {
        this.seriesNum = seriesNum;
    }

    public int getSeriesNum() {
        return this.seriesNum;
    }

    /********************************* 1. 标识符差异配置 *********************************/
    @Override
    public String escString(String s) {
        return StringUtils.replace(StringUtils.replace(s, "\\", "\\\\"), "'", "\\'");
    }

    @Override
    public String getLeftQuoteChar() {
        return "`";
    }

    @Override
    public String quoteSingle(String s) {
        return String.format("`%s`", StringUtils.replace(s, "`", ""));
    }

    @Override
    public String unquoteSingleEntity(String s) {
        return SqlUtil.removeRepeatedSymbolAtHeadAndTail(s, '`');
    }

    /********************************* 2. TODO *********************************/


    @Override
    public boolean supportWith() {
        return this.majorVersion == 0L || this.majorVersion > 7L;
    }

    @Override
    public TypedVal setPage(TypedVal query, Long offset, Long limit) {
        if (query == null) {
            return new TypedVal();
        }
        if (StringUtils.isEmpty(query.getStr())) {
            return query;
        }
        String sql = query.getStr();
        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
        sqlBuilder.append(sql);
        if (offset.longValue() == 0L) {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(limit);
        } else {
            sqlBuilder.append(" LIMIT ");
            sqlBuilder.append(offset);
            sqlBuilder.append(",");
            sqlBuilder.append(limit);
        }
        return new TypedVal(sqlBuilder.toString());
    }


    public String toString(String expr) {
        return String.format("cast(%s as char)", new Object[]{expr});
    }


    public String toInteger(String expr) {
        return String.format("cast(%s AS signed)", new Object[]{expr});
    }


    public String parseNumber(String expr) {
        return String.format("CASE WHEN (%s) in ('', ' ') THEN NULL ELSE cast(%s AS decimal(38,10)) END", new Object[]{expr, expr});
    }


    public String unixNumberToTime(String expr) {
        return String.format("from_unixtime(%s)", new Object[]{expr});
    }


    public String parseBool(String expr) {
        return String.format("CASE WHEN (%s) in ('true', 'True', 'TRUE', 'T', 't', 1) THEN TRUE WHEN (%s) IN ('false', 'false', 'FALSE', 'F', 'f', 0) THEN FALSE END", new Object[]{expr, expr});
    }


    public String parseTime(String expr, String dateFormat) {
        if (dateFormat.equals("'%Y'")) {
            return String.format("str_to_date(concat(%s, '-01-01'), '%%Y-%%m-%%d')", new Object[]{expr});
        }
        if (dateFormat.equals("'%Y-%m'")) {
            return String.format("str_to_date(concat(%s, '-01'), '%%Y-%%m-%%d')", new Object[]{expr});
        }
        if (dateFormat.contains("%Y") || dateFormat.contains("%m") || dateFormat.contains("%d")) {
            String toRet = String.format("str_to_date(replace(%s, '-00', '-01'), %s)", new Object[]{expr, dateFormat});
            if (!dateFormat.contains("%m") || !dateFormat.contains("%d")) {
                toRet = String.format("cast(replace(%s,'-00','-01') as datetime)", new Object[]{toRet});
            }
            return toRet;
        }
        return String.format("str_to_date(%s, %s)", new Object[]{expr, dateFormat});
    }


    public String parseDate(String expr, String dateFormat) {
        if (!dateFormat.contains("%m") || !dateFormat.contains("%d")) {
            return String.format("cast(%s as date)", new Object[]{parseTime(expr, dateFormat)});
        }
        return parseTime(expr, dateFormat);
    }


    public String timeToDate(String expr) {
        return String.format("cast(%s as date)", new Object[]{expr});
    }


    public String dateToTime(String expr) {
        return String.format("cast(%s as datetime)", new Object[]{expr});
    }


    public String parseJson(String expr) {
        return String.format("if(json_valid(%1$s), cast(%1$s as json), null)", new Object[]{expr});
    }


    public String jsonExtract(String parent, String sub) {
        return String.format("%s->'$.%s'", new Object[]{parent, sub});
    }


    @Override
    public Object jodaToNativeParseFormat(String jodaFormat) {
        return jodaFormat
                .replace("yyyy", "%Y")
                .replace("YYYY", "%Y")
                .replace("yy", "%y")
                .replace("YY", "%y")
                .replace("MMM", "%b")
                .replace("MM", "%m")
                .replace("dd", "%d")
                .replace("HH", "%H")
                .replace("hh", "%h")
                .replace("mm", "%i")
                .replace("ss", "%s")
                .replace("SSS", "%f")
                .replace("SS", "%f")
                .replace("S", "%f")
                .replace("'T'", "T")
                .replace("'Z'", "Z");
    }

    @Override
    public boolean isJsonAvailable() {
        return true;
    }

    @Override
    public boolean canGroupByIndex() {
        return true;
    }

    @Override
    public String quoteGroupByFieldName(TypedVal val) {
        return val.getStr();
    }

    @Override
    public boolean isAliasAmbiguousInJoin() {
        return true;
    }

    public static String differentiateByIndex(String expr, int index) {
        if (index == 0 || "null".equals(expr)) {
            return expr;
        }
        return String.format("(CASE WHEN %d THEN %s END)", new Object[]{Integer.valueOf(index), expr});
    }

}
